package net.ewon.avatar.agent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import net.ewon.avatar.common.Deployment;
import net.ewon.avatar.common.Host;
import net.ewon.avatar.common.exception.InvalidRequestException;
import net.ewon.avatar.common.exception.ServiceDependencyException;
import net.ewon.avatar.common.pkg.AvatarPackage;
import net.ewon.avatar.common.pkg.AvatarPackageFile;
import net.ewon.avatar.common.pkg.AvatarPackageSource;
import net.ewon.avatar.common.pkg.AvatarPackageWrapper;
import net.ewon.avatar.common.pkg.Version;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

@Controller
@RequestMapping("/AvatarAgent")
public class AvatarAgentController {

	private static final Logger logger = Logger
			.getLogger(AvatarAgentController.class);

	// Must add / at the end of url otherwise the dot(.) will be used for file
	// extension, which is not what we need.
	private static final String GET_AVATAR_PACKAGE_URL = "http://%s:%d/AvatarPackageMgmtService/AvatarPkgMgmtService/name/{name}/version/{version}/";
	private static final String GET_FILE_URL = "http://%s:%d/AvatarPackageMgmtService/AvatarPkgMgmtService/getfile";
 
	@Resource
	private RestTemplate restTemplate;

	@Resource(name="avatarPackageRepo")
	private AvatarPackageRepo avatarPackageRepo;
 

	@RequestMapping("hello")
	public @ResponseBody
	String hello() {
		return "Hello world!";
	}

	@RequestMapping(value = "/missingPackages", method = RequestMethod.POST)
	public @ResponseBody
	AvatarPackage[] getMissingAvatarPackages(
			@RequestBody AvatarPackage[] avatarPackages) {

		List<AvatarPackage> missingPackages = new ArrayList<AvatarPackage>();

		for (AvatarPackage avatarPackage : avatarPackages) {
			if (!avatarPackageRepo.exists(avatarPackage.getName(),
					avatarPackage.getVersion())) {
				missingPackages.add(avatarPackage);
			}
		}
		return missingPackages.toArray(new AvatarPackage[0]);
	}

	@RequestMapping(value = "/sync", method = RequestMethod.POST)
	public @ResponseBody
	String syncAvatarPackage(
			@RequestBody AvatarPackageWrapper avatarPackageWrapper)
			throws ServiceDependencyException {

		String pkgName = avatarPackageWrapper.getPkgName();
		Version version = avatarPackageWrapper.getVersion();
		AvatarPackageSource avatarPackageSource = avatarPackageWrapper
				.getAvatarPackageSource();

		logger.info("Sync package with name: " + pkgName + ", version: "
				+ version + " from source " + avatarPackageSource.toString());

		Map<String, String> urlVariables = getUrlVariables(pkgName, version);

		logger.info("the get avatar package url:"
				+ getNormalizedUrl(GET_AVATAR_PACKAGE_URL, avatarPackageSource));
		AvatarPackage avatarPackage = restTemplate.getForObject(
				getNormalizedUrl(GET_AVATAR_PACKAGE_URL, avatarPackageSource),
				AvatarPackage.class, urlVariables);
		if (avatarPackage == null) {
			throw new ServiceDependencyException("Package " + pkgName
					+ " with version[" + version + "] doesn't exit in repo.");
		}
		List<AvatarPackageFile> avatarPackageFiles = new ArrayList<AvatarPackageFile>();
		String get_file_url = getNormalizedUrl(GET_FILE_URL,
				avatarPackageSource);
		logger.info("the get file:" + get_file_url);
		for (String fileName : avatarPackage.getFileIds()) {
			ResponseEntity<byte[]> data;
			try {
				data = restTemplate.postForEntity(get_file_url, fileName,
						byte[].class, urlVariables);
				avatarPackageFiles.add(new AvatarPackageFile(fileName, data
						.getBody()));
				logger.debug("file data=" + new String(data.getBody()));
			} catch (RestClientException e) {
				logger.error("Failed to sync the file " + fileName);
			}
		}

		avatarPackageRepo.install(avatarPackageFiles);

		return "ok";
	}

	@RequestMapping(value = "/deploy", method = RequestMethod.POST)
	public @ResponseBody
	String deploy(@RequestBody Deployment deployment)
			throws InvalidRequestException, ServiceDependencyException {
		validateDeployment(deployment);
		
		String envName = deployment.getEnvironment().getName();
		Set<AvatarPackage> avatarPackages = deployment.getAvatarPackages();
		Set<Host> hosts = deployment.getEnvironment().getHosts();
		
		
		return null;
	}
	
	private void validateDeployment(Deployment deployment) throws InvalidRequestException {
		if(deployment==null) {
			throw new InvalidRequestException("The deployment can't be null."); 
		}
		
		if(deployment.getEnvironment()==null) {
			throw new InvalidRequestException("The environment for deployment "+ deployment.getId() + " can't be null."); 
		}
		
		if(StringUtils.isBlank(deployment.getEnvironment().getName())) {
			throw new InvalidRequestException("The environment name for deployment "+ deployment.getId() + " can't be blank (null or empty)."); 
		}
		
		if(CollectionUtils.isEmpty(deployment.getEnvironment().getHosts())) {
			throw new InvalidRequestException("The hosts of the environment for deployment "+ deployment.getId() + " can't be empty."); 
		} 
		
	}

	private Map<String, String> getUrlVariables(String pkgName, Version version) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("name", pkgName);
		map.put("version", version.getVersion());
		return map;
	}

	private String getNormalizedUrl(String url,
			AvatarPackageSource avatarPackageSource) {
		return String.format(url, avatarPackageSource.getHost(),
				avatarPackageSource.getPort());
	}

}